#include "StdAfx.h"
#include "ForestLoader.h"
#include "PagedEntity.h"
#include "PagedTreeBlock.h"

using namespace std;

namespace FEngine
{

	ForestLoader::ForestLoader(int _blockSize, int _numberBlock, Vector2 _pos, float _maxViewDistance, float _impostorApparitionDistance)
		: mBlockSize(_blockSize), mNumberBlock(_numberBlock), mPosition(_pos)
	{
		mPagedObject				= NULL;
		mMaximumViewDistance		= _maxViewDistance;
		mImpostorApparitionDistance	= _impostorApparitionDistance;
		
		mVertexBufferImpostor		= NULL;
		mIndexBufferImpostor		= NULL;
		mVertexDeclarationImpostor	= NULL;

		if (mMaximumViewDistance < 0)
			mMaximumViewDistance = 0;
		if (mImpostorApparitionDistance < 0)
			mImpostorApparitionDistance = 0;
		if (mMaximumViewDistance < mImpostorApparitionDistance)
			mImpostorApparitionDistance = mMaximumViewDistance;

		Vector2 pos = Vector2(mPosition);
		mSize = Vector2(1.0f, 1.0f) * (float)(mBlockSize * mNumberBlock);
		Vector3 minPos, maxPos;

		for (int y = 0; y < mNumberBlock; ++y)
		{
			for (int x = 0; x < mNumberBlock; ++x)
			{
				minPos = Vector3(pos.x, 0, pos.y);
				maxPos = Vector3(pos.x, 0, pos.y) + Vector3((float)mBlockSize, 1.0f, (float)mBlockSize);
				mPagedBlockVector.push_back(new PagedTreeBlock(minPos, maxPos, mMaximumViewDistance, mImpostorApparitionDistance));
				pos.x += mBlockSize;
			}
			pos.y += mBlockSize;
			pos.x = _pos.x;
		}
		
		// Load the vertex declarations
		CoreEngine::getInstance()->getDeviceInterface()->CreateVertexDeclaration(VertexDeclaration, &mVertexDeclarationObject);
		CoreEngine::getInstance()->getDeviceInterface()->CreateVertexDeclaration(VertexDeclarationImpostor, &mVertexDeclarationImpostor);
	}

	ForestLoader::~ForestLoader()
	{
		if (mPagedObject)
			delete mPagedObject;
		if (mVertexBufferImpostor)
			mVertexBufferImpostor->Release();
		if (mVertexDeclarationImpostor)
			mVertexDeclarationImpostor->Release();
		if (mVertexDeclarationObject)
			mVertexDeclarationObject->Release();
		if (mIndexBufferImpostor)
			mIndexBufferImpostor->Release();

		for (std::vector<PagedTreeBlock*>::iterator it = mPagedBlockVector.begin(); it != mPagedBlockVector.end(); ++it)
			delete *it;
	}

	void ForestLoader::createObject(const std::string& _fileName, const std::string& _textureName)
	{
		if (mPagedObject)
			return;

		mPagedObject = new PagedEntity(_fileName, _textureName, (mMaximumViewDistance != mImpostorApparitionDistance));
	}
	
	void ForestLoader::addObject(const Vector3& _pos, const Vector3& _rot, const Vector3& _sca)
	{
		// Check if the object in inside the limits of the loader
		if (_pos.x < mPosition.x || _pos.x > (mPosition + mSize).x ||
			_pos.z < mPosition.y || _pos.z > (mPosition + mSize).y)
			return;
		
		// Get the page associated with the object position
		int posX = (int)(_pos.x - mPosition.x) / mBlockSize;
		int posY = (int)(_pos.z - mPosition.y) / mBlockSize;

		mPagedBlockVector[posX + mNumberBlock * posY]->addObject(_pos, _rot, _sca, mPagedObject->getImpostorScale());
	}

	void ForestLoader::addObject(const Matrix4& _worldMat)
	{
		Vector3 pos(_worldMat._41, _worldMat._42, _worldMat._43);

		// Check if the object in inside the limits of the loader
		if (pos.x < mPosition.x || pos.x > (mPosition + mSize).x ||
			pos.z < mPosition.y || pos.z > (mPosition + mSize).y)
			return;
		
		// Get the page associated with the object position
		int posX = (int)(pos.x - mPosition.x) / mBlockSize;
		int posY = (int)(pos.z - mPosition.y) / mBlockSize;

		mPagedBlockVector[posX + mNumberBlock * posY]->addObject(_worldMat);
	}

	int	ForestLoader::getNumberObject() const
	{
		int number = 0;

		for (vector<PagedTreeBlock*>::const_iterator it = mPagedBlockVector.begin(); it != mPagedBlockVector.end(); ++it)
			number += (*it)->getNumberObject();

		return number;
	}

	int	ForestLoader::getNumberVisibleObject() const
	{
		int number = 0;

		for (vector<PagedTreeBlock*>::const_iterator it = mPagedBlockVector.begin(); it != mPagedBlockVector.end(); ++it)
			if ((*it)->isVisible())
				number += (*it)->getNumberVisibleObject();

		return number;
	}

	int	ForestLoader::getNumberImpostor() const
	{
		int number = 0;

		for (vector<PagedTreeBlock*>::const_iterator it = mPagedBlockVector.begin(); it != mPagedBlockVector.end(); ++it)
			number += (*it)->getNumberImpostor();

		return number;
	}

	int	ForestLoader::getNumberVisibleImpostor() const
	{
		int number = 0;

		for (vector<PagedTreeBlock*>::const_iterator it = mPagedBlockVector.begin(); it != mPagedBlockVector.end(); ++it)
			if ((*it)->isVisible())
				number += (*it)->getNumberVisibleImpostor();

		return number;
	}

	int	ForestLoader::getNumberPage() const
	{
		return mPagedBlockVector.size();
	}

	int	ForestLoader::getNumberVisiblePage() const
	{
		int number = 0;

		for (vector<PagedTreeBlock*>::const_iterator it = mPagedBlockVector.begin(); it != mPagedBlockVector.end(); ++it)
			if ((*it)->isVisible())
				number++;

		return number;
	}
			
	void ForestLoader::update(double _elapsedTime)
	{
		for (vector<PagedTreeBlock*>::iterator it = mPagedBlockVector.begin(); it != mPagedBlockVector.end(); ++it)
			(*it)->update(_elapsedTime);
	}
	
	void ForestLoader::render(double _elapsedTime)
	{
		_renderObject(_elapsedTime);
		_renderImpostor(_elapsedTime);
	}

	void ForestLoader::_renderObject(double _elapsedTime)
	{
		// Get the list of every instance's matrix
		std::vector<Matrix4> vectorObjectInstance;
		for (vector<PagedTreeBlock*>::iterator it = mPagedBlockVector.begin(); it != mPagedBlockVector.end(); ++it)
			(*it)->getVectorVisibleObjectInstance(&vectorObjectInstance);

		if (vectorObjectInstance.size() == 0)
			return;

		LPD3DXEFFECT shader = CoreEngine::getInstance()->getShaderEffect();
		IDirect3DDevice9* device = CoreEngine::getInstance()->getDeviceInterface();

		if (shader->SetTechnique(shader->GetTechniqueByName("Mesh")) != D3D_OK)
			throw FEngineException("ForestLoader", "render", "Unknown shader technique");

		LPDIRECT3DTEXTURE9 texture;
		mPagedObject->getTexture(&texture);
		if (shader->SetTexture(shader->GetParameterByName(NULL, "gMeshTexture"), texture) != D3D_OK)
			throw FEngineException("MeshObject", "render", "Unknown shader parameter");
		
		LPDIRECT3DVERTEXBUFFER9 vertexBuffer;
		mPagedObject->getVertexBuffer(&vertexBuffer);
		LPDIRECT3DINDEXBUFFER9 indexBuffer;
		mPagedObject->getIndexBuffer(&indexBuffer);
		
		device->SetVertexDeclaration(mVertexDeclarationObject);
		device->SetStreamSource(0, vertexBuffer, 0, sizeof(FE_VERTEX));
		device->SetIndices(indexBuffer);
		
		D3DXMATRIX matWVP;
		D3DXMATRIX matVP = CoreEngine::getInstance()->getCamera()->_getMatrixView() * CoreEngine::getInstance()->getCamera()->_getMatrixProjection();
		unsigned int cPasses, iPass;
		shader->Begin(&cPasses, 0);
		for (iPass= 0; iPass< cPasses; ++iPass)
		{
			shader->BeginPass(iPass);

			// Set the world matrix of each object before rendering it
			for (vector<Matrix4>::iterator it = vectorObjectInstance.begin(); it != vectorObjectInstance.end(); ++it)
			{
				matWVP = (*it) * matVP;
				if (shader->SetMatrix(shader->GetParameterByName(NULL, "gWorldViewProj"), &matWVP) != D3D_OK)
					throw FEngineException("ForestLoader", "render", "Unknown shader parameter");
				shader->CommitChanges();

				CoreEngine::getInstance()->getDeviceInterface()->DrawIndexedPrimitive(D3DPT_TRIANGLELIST, 0, 0, mPagedObject->getNumberVertices(), 0, mPagedObject->getNumberPrimitives());
			}
			
			shader->EndPass();
		}
		shader->End();
	}
	
	void ForestLoader::_renderImpostor(double _elapsedTime)
	{
		_setImpostorBuffers();
		if (mNumberPrimitivesImpostor == 0 || mNumberVerticesImpostor == 0)
			return;

		LPD3DXEFFECT shader = CoreEngine::getInstance()->getShaderEffect();
		IDirect3DDevice9* device = CoreEngine::getInstance()->getDeviceInterface();
		
		D3DXMATRIX matVP = CoreEngine::getInstance()->getCamera()->_getMatrixView() * CoreEngine::getInstance()->getCamera()->_getMatrixProjection();
		
		if (shader->SetTechnique(shader->GetTechniqueByName("Impostor")) != D3D_OK)
			throw FEngineException("GrassLoader", "render", "Unknown shader technique");
		if (shader->SetMatrix(shader->GetParameterByName(NULL, "gViewProj"), &matVP) != D3D_OK)
			throw FEngineException("GrassLoader", "render", "Unknown shader parameter");
		LPDIRECT3DTEXTURE9 impostorTexture;
		mPagedObject->getImpostorTexture(&impostorTexture);
		if (shader->SetTexture(shader->GetParameterByName(NULL, "gMeshTexture"), impostorTexture) != D3D_OK)
			throw FEngineException("GrassLoader", "render", "Unknown shader parameter ");
		shader->CommitChanges();

		device->SetVertexDeclaration(mVertexDeclarationImpostor);
		device->SetStreamSource(0, mVertexBufferImpostor, 0, sizeof(FE_IMPOSTOR));
		device->SetIndices(mIndexBufferImpostor);
		
		unsigned int cPasses, iPass;
		shader->Begin(&cPasses, 0);
		for (iPass= 0; iPass< cPasses; ++iPass)
		{
			shader->BeginPass(iPass);
			CoreEngine::getInstance()->getDeviceInterface()->DrawIndexedPrimitive(D3DPT_TRIANGLELIST, 0, 0, mNumberVerticesImpostor, 0, mNumberPrimitivesImpostor);
			shader->EndPass();
		}
		shader->End();
	}
	
	void ForestLoader::_setImpostorBuffers()
	{
		// Release the previous buffers
		if (mVertexBufferImpostor)
			mVertexBufferImpostor->Release();
		mVertexBufferImpostor = NULL;

		if (mIndexBufferImpostor)
			mIndexBufferImpostor->Release();
		mIndexBufferImpostor = NULL;
		
		// Get the list of vertices
		vector<FE_IMPOSTOR*> vectorImpostorInstance;
		for (vector<PagedTreeBlock*>::iterator it = mPagedBlockVector.begin(); it != mPagedBlockVector.end(); ++it)
			(*it)->getVectorVisibleImpostorInstance(&vectorImpostorInstance);

		mNumberVerticesImpostor = vectorImpostorInstance.size();
		mNumberPrimitivesImpostor = mNumberVerticesImpostor / 2;
		int numberBillboard = mNumberPrimitivesImpostor / 2;
		
		if (vectorImpostorInstance.size() == 0)
			return;
		
		unsigned int verticesSize = mNumberVerticesImpostor * sizeof(FE_IMPOSTOR);
		unsigned int indexesSize = numberBillboard * 6 * sizeof(short);
		
		// Create the vertex buffer
		CoreEngine::getInstance()->getDeviceInterface()->CreateVertexBuffer(verticesSize, D3DUSAGE_WRITEONLY, 0, D3DPOOL_MANAGED, &mVertexBufferImpostor, NULL);
		
		// Create the index buffer
		CoreEngine::getInstance()->getDeviceInterface()->CreateIndexBuffer(indexesSize, D3DUSAGE_WRITEONLY, D3DFMT_INDEX16, D3DPOOL_MANAGED, &mIndexBufferImpostor, NULL);
		
		
		// Copy the vertices into memory
		FE_IMPOSTOR* vertices;
		mVertexBufferImpostor->Lock(0, 0, (void**)&vertices, 0);
		for (int n = 0; n < mNumberVerticesImpostor; ++n)
			vertices[n] = *vectorImpostorInstance[n];
		mVertexBufferImpostor->Unlock();
		
		// Copy the indexes into memory
		short* indexes;
		mIndexBufferImpostor->Lock(0, 0, (void**)&indexes, 0);
		for (int n = 0; n < numberBillboard; ++n)
		{
			indexes[6 * n + 0] = 4 * n + 0;
			indexes[6 * n + 1] = 4 * n + 1;
			indexes[6 * n + 2] = 4 * n + 2;
			indexes[6 * n + 3] = 4 * n + 0;
			indexes[6 * n + 4] = 4 * n + 2;
			indexes[6 * n + 5] = 4 * n + 3;
		}
		mIndexBufferImpostor->Unlock();
	}

} // namespace FEngine
